﻿/***************************************************/
/*** file:IMLogicHandle.cs                       ***/
/*** date:2010-08-07                             ***/
/*** author:huas                                 ***/
/*** function:implement Logic of IM              ***/
/***************************************************/



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using IM.Common;

namespace IM.Logic
{
    class IMLogicHandle 
    {
        private IMLogic m_handleLogic;
        private RingBuffer m_c2sRingBuffer;
        private NetHeader m_netHeader;
        private int m_nThreadIndex;
        private byte[] m_byBuffer;
        private byte[] m_byTempC2SBuffer;
        private byte[] m_byTempParseBuffer;
        private AutoResetEvent m_dataRecvEvent;
        private ProtocolMgr m_protocolMgr;
        private IMDatabase m_imDatabase;
        private SessionMgr m_sessionMgr;
        private byte[] m_tmpBuf;

        public IMLogicHandle(IMLogic handleLogic)
        {
            m_handleLogic = handleLogic;
            m_c2sRingBuffer = new RingBuffer(0);
            m_byBuffer = new byte[10240];
            m_byTempC2SBuffer = new byte[10240];
            m_byTempParseBuffer = new byte[10240];
            m_netHeader = new NetHeader();
            m_dataRecvEvent = new AutoResetEvent(false);
            m_protocolMgr = new ProtocolMgr(); //所有协议类预分配,避免处理协议时new操作。

            m_tmpBuf = new byte[NetHeader.SIZE];
            m_sessionMgr = SessionMgr.Instance();
            m_imDatabase = new IMDatabase();
        }

        public void CreateThread(int nIndex)
        {
            m_nThreadIndex = nIndex;
            ThreadStart ts = new ThreadStart(Run);
            Thread t = new Thread(ts);
            t.Start();
        }

        private void IsShouldBlock()
        {
            //如果缓冲区没有数据，阻塞当前线程
            if (m_c2sRingBuffer.IsEmpty())
            {
                m_dataRecvEvent.WaitOne();
            }
        }

        private void Run()
        {
            int nSize = 0;
            while (true)
            {
                IsShouldBlock();
                nSize = m_byBuffer.Length;
                int nRet = m_c2sRingBuffer.GetOnePack(m_byBuffer, ref nSize);
                if (nRet < 0)
                {
                    continue;
                }
                if (nSize > 0)
                {
                    ProcessRequest();
                }
            }
        }

        private void WriteInfoLog(string strInfo)
        {
 
        }

        private void SetAccountId(int nAccountId)
        {
            NetHeader netHeader = m_netHeader;
            netHeader.m_userData = nAccountId;
            netHeader.m_nInnerFlag = InnerFlag.INNERFLAG_SET_USER_DATA;

            netHeader.ToBytes(m_tmpBuf, 0);
            SendToClient(m_tmpBuf, m_tmpBuf.Length, ref netHeader);
        }

        public int GetAccountId()
        {
            int accountId = (int)(m_netHeader.m_userData & 0x00000000ffffffff);
            return accountId;
        }

        public void SetNetHeader(ref NetHeader netHeader)
        {
            m_netHeader = netHeader;
        }

        private int ProcessRequest()
        {
            int nRet = 0;
            m_netHeader.FromBytes(m_byBuffer, 0);
            Array.Copy(
                m_byBuffer,
                NetHeader.SIZE,
                m_byTempParseBuffer,
                0, 
                m_byBuffer.Length - NetHeader.SIZE
                );
          
            IMProtocolDef protocol = (IMProtocolDef)BitConverter.ToUInt16(m_byTempParseBuffer, 4);
            switch (protocol)
            {
                case IMProtocolDef.im_c2s_account_verify:
                    //用户登录验证
                    nRet = ProcessUserLogin(m_byTempParseBuffer);
                    //处理失败，关闭socket
                    if (nRet != 0)
                    {
                        CloseSocket();
                    }
                    break;
                default:
                    //根据账号id找到进行处理的session;
                    int nAccountId = GetAccountId();
                    Session session = null;
                    m_sessionMgr.FindSession(nAccountId, ref session);
                    if (session != null)
                    {
                        nRet = session.ProcessRequest(protocol, m_byTempParseBuffer);
                        //处理失败，关闭对应的socket
                        if (nRet != 0)
                        {
                            CloseSocket();
                        }
                    }
                    WriteInfoLog("unsuport protocol");
                    break;
            }
            return nRet;

        }

        public int SendToClient(byte[] szData, int nDataLen, ref NetHeader netHeader)
        {
            if (m_handleLogic != null)
            {
                return m_handleLogic.OnSendToClient(szData, nDataLen, ref netHeader);
            }
            return -1;
        }
        private void Wakeup()
        {
            m_dataRecvEvent.Set();
        }

        //return -1:nodata
        public int OnReceiveFromClient(byte[] bData, int nBeginIndex, UInt32 nDataLen)
        {
            if (nDataLen <= 0)
            {
                return -1;
            }
            Array.Copy(bData, nBeginIndex, m_byTempC2SBuffer, 0, nDataLen);
            int nRet =  m_c2sRingBuffer.AppendOnePack(m_byTempC2SBuffer, (int)nDataLen);
            
            Wakeup();   //唤醒逻辑处理线程
            return nRet;
        }

        private int ProcessUserLoginAgain(c2s_account_verify account_verify, Session session)
        {
            if (session == null)
            {
                return -1;
            }
            //通知先前登录的用户下线。
            //构造登出包，分发到已经登录的用户所在的线程上去.
            c2s_account_logout logout_msg = m_protocolMgr.c2s_account_logout;
            logout_msg.szAccount = account_verify.szAccount;
            logout_msg.nLogoutReason = (int)IMResult.err_im_account_login_other;
           
            byte[] bPackData = logout_msg.ToBytes();
            int nRet = session.ProcessRequest(IMProtocolDef.im_c2s_account_logout, bPackData);
            if (nRet == 0)
            {
                m_sessionMgr.DeleteSession(session);
            }
            return 0;
        }

        private void CloseSocket()
        {
            m_netHeader.m_nInnerFlag = InnerFlag.INNERFLAG_LOGIC_REQUEST_CLOSE_SOCKET;

            byte[] bData = new byte[NetHeader.SIZE];
            m_netHeader.ToBytes(m_tmpBuf, 0);
            SendToClient(m_tmpBuf, m_tmpBuf.Length, ref m_netHeader);
        }

        private int ProcessUserLogin(byte[] byPackData)
        {
            c2s_account_verify account_verify = m_protocolMgr.c2s_account_verify;
            account_verify.FromBytes(byPackData);

            Session session = null;
            int nAccountId = GetAccountId();
            int nRet = m_sessionMgr.FindSession(nAccountId, ref session);
            //该用户已经存在，处理用户重复登录
            //(例如用户已经登录了，但没有下线。同时又在别的地方登录，这时候要把先前的登录下线)
            if (nRet == 0)
            {
                ProcessUserLoginAgain(account_verify, session);
            }
            int account_id = 0;
            bool bVerifyResult = m_imDatabase.VerifyAccount(account_verify.szAccount, account_verify.szPassword, ref account_id);
            s2c_account_verify_result account_verify_result = m_protocolMgr.s2c_account_verify_result;
            account_verify_result.nVerifyResult = (int)IMResult.err_im_account_or_pwd;
            account_verify_result.szAccount = account_verify.szAccount;

            //登录成功，获取用户详细信息。分配一个Session
            if (bVerifyResult)
            {
                account_verify_result.nVerifyResult = (int)IMResult.im_success;
                m_imDatabase.GetAccountDetail(ref account_verify_result, account_id);

                m_sessionMgr.CreateSession(account_id, ref session);
                if (session == null)
                {
                    nRet = -2;
                }
                else
                {
                    session.Initialize(m_protocolMgr, this);
                    session.Account = account_verify.szAccount;
                    session.AccountId = account_id;
                    session.AccountStatus = (IMAccountStatus)account_verify.uAccountStatus;
                    session.NetHeader = m_netHeader;
                    session.ThreadIndex = m_nThreadIndex;
                    SetAccountId(account_id);
                    nRet = 0;
                }
            }
            else
            {
                nRet = -1;
            }
            byte[] bReturnData = account_verify_result.ToBytes();
            //发送验证结果给客户端
            SendToClient(bReturnData, bReturnData.Length, ref m_netHeader);
            return nRet;
        }
    }
}
